package cn.redragon.soa.common.config.web;


import static cn.redragon.soa.common.constant.Constants.DateTimePattern.FMT_DATE;
import static cn.redragon.soa.common.constant.Constants.DateTimePattern.FMT_DATE_TIME;
import static cn.redragon.soa.common.constant.Constants.DateTimePattern.FMT_DATE_TIME_TZ_JODA;
import static cn.redragon.soa.common.constant.Constants.DateTimePattern.FMT_TIME;

import cn.redragon.soa.common.web.JacksonXssDeserializer;
import cn.redragon.soa.common.web.JacksonXssSerializer;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import org.springframework.boot.jackson.JsonComponentModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonObjectMapperConfig {

    public static ObjectMapper generateObjectMapper() {
        return getDefaultObjectMapper(FMT_DATE_TIME);
    }

    public static ObjectMapper generateObjectMapperWithTZ() {
        return getDefaultObjectMapper(FMT_DATE_TIME_TZ_JODA);
    }

    public static ObjectMapper generateObjectMapperWith(String dateTimePattern) {
        return getDefaultObjectMapper(dateTimePattern);
    }

    public static ObjectMapper getDefaultObjectMapper(String dateTimePattern) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        mapper.registerModule(new Jdk8Module());
        mapper.registerModule(new ParameterNamesModule());
        mapper.registerModule(new JsonComponentModule());
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        JavaTimeModule javaTimeModule = new JavaTimeModule();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(dateTimePattern);
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(FMT_DATE);
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern(FMT_TIME);
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));

        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));

        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
        mapper.registerModule(javaTimeModule);

        return mapper;
    }

    public static ObjectMapper withXss(ObjectMapper mapper) {
        SimpleModule xssModule = new SimpleModule("XssStringJsonModule");
        xssModule.addDeserializer(String.class, new JacksonXssDeserializer());
        xssModule.addSerializer(String.class, new JacksonXssSerializer());
        mapper.registerModule(xssModule);

        return mapper;
    }

    public static ObjectMapper withNamingStrategy(ObjectMapper mapper, PropertyNamingStrategy namingStrategy) {
        mapper.setPropertyNamingStrategy(namingStrategy);
        return mapper;
    }

    @Bean("commonObjectMapper")
    public ObjectMapper objectMapper() {
        return generateObjectMapper();
    }

}
